// Fluxions 3D Engine
// staticmodel.hpp
// Copyright (C) 2000-2011 Jonathan Metzgar
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#ifndef FLUXIONS_STATICMODEL_HPP
#define FLUXIONS_STATICMODEL_HPP

#include <string>
#include <vector>
#include <stdio.h>
#include "math.hpp"
#include "opengl.hpp"

namespace Fluxions
{
	struct Vertex64
	{
		Vector3f position;
		Vector3f normal;
		TVector4<GLubyte> color;
		Vector2f texcoord1;
		Vector3f texcoord2;
		Vector4f texcoord3;
	};

	struct Surface
	{
		GLenum mode;
		GLint first;
		GLsizei count;
		std::string materialName;
	};

	class StaticModel
	{
	public:
		StaticModel();
		~StaticModel();

		bool Save(const std::string &filename);
		bool Load(const std::string &filename);

		void Render(int surface=-1);
		void RenderLines(int surface=-1);
		void RenderPoints(int surface=-1);

		void Clear();
		void Resize(int vertexCount, int indexCount, int surfaceCount=1);
		void CreateSimpleModel(int vertexCount, int indexCount, int surfaceCount=1);

		std::vector<Vertex64> Vertices;
		std::vector<GLushort> Indices;
		std::vector<Surface> Surfaces;
	};


	StaticModel::StaticModel()
	{
	}


	StaticModel::~StaticModel()
	{
	}


	bool StaticModel::Save(const std::string &filename)
	{
		int size;
		char fileID[] = "FLUXIONSMODEL";
		int fileVersion = 1;
		FILE *fout = fopen(filename.c_str(), "wb");
		if (!fout)
			return false;
		// Write Header
		fwrite(fileID, 1, 13, fout);
		fwrite(&fileVersion, 1, sizeof(int), fout);
		size = Vertices.size();
		fwrite(&size, 1, sizeof(int), fout);
		size = Indices.size();
		fwrite(&size, 1, sizeof(int), fout);
		size = Surfaces.size();
		fwrite(&size, 1, sizeof(int), fout);

		// Write Data
		fwrite(&Vertices[0], Vertices.size(), sizeof(Vertex64), fout);
		fwrite(&Indices[0], Indices.size(), sizeof(GLushort), fout);
		fwrite(&Surfaces[0], Surfaces.size(), sizeof(Surface), fout);

		fclose(fout);
		return true;
	}


	bool StaticModel::Load(const std::string &filename)
	{
		int size1, size2, size3;
		char fileID[] = "FLUXIONSMODEL";
		int fileVersion = 1;
		FILE *fin = fopen(filename.c_str(), "rb");
		fread(fileID, 1, 13, fin);
		if (strcmp(fileID, "FLUXIONSMODEL") != 0)
		{
			fclose(fin);
			return false;
		}
		fread(&fileVersion, 1, sizeof(int), fin);
		if (fileVersion != 1)
		{
			fclose(fin);
			return false;
		}

		fread(&size1, 1, sizeof(int), fin);
		fread(&size2, 1, sizeof(int), fin);
		fread(&size3, 1, sizeof(int), fin);
		Resize(size1, size2, size3);
		fread(&Vertices[0], Vertices.size(), sizeof(Vertex64), fin);
		fread(&Indices[0], Indices.size(), sizeof(GLushort), fin);
		fread(&Surfaces[0], Surfaces.size(), sizeof(Surface), fin);

		fclose(fin);
		return true;
	}


	void StaticModel::Render(int surface)
	{
		// Set range for individual surface drawing if applicable
		int first, last;
		if (surface < 0)
		{
			first = 0;
			last = Surfaces.size() - 1;
		}
		else
		{
			first = last = surface;
		}

		// Draw each surface
		for (int i = first; i <= last; ++i)
		{
			//glDrawElements(Surfaces[i].mode, Surfaces[i].count, GL_UNSIGNED_SHORT, &indices[Surfaces[i].first]);
			glBegin(Surfaces[i].mode);
			for (int j = Surfaces[i].first; j < Surfaces[i].count; ++j)
			{
				Vertex64 *vtx = &Vertices[Indices[j]];
				glColor4ubv(vtx->color.v);
				glMultiTexCoord2fv(GL_TEXTURE0, vtx->texcoord1.v);
				glMultiTexCoord3fv(GL_TEXTURE1, vtx->texcoord2.v);
				glMultiTexCoord3fv(GL_TEXTURE2, vtx->texcoord3.v);
				glNormal3fv(vtx->normal.v);
				glVertex3fv(vtx->position.v);
			}
			glEnd();
		}
	}


	void StaticModel::RenderLines(int surface)
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		Render(surface);
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}


	void StaticModel::RenderPoints(int surface)
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
		Render(surface);
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}


	void StaticModel::CreateSimpleModel(int vertexCount, int indexCount, int surfaceCount)
	{
		Clear();
		Resize(vertexCount, indexCount, surfaceCount);
	}


	void StaticModel::Resize(int vertexCount, int indexCount, int surfaceCount)
	{
		if (vertexCount > 0)
			Vertices.resize(vertexCount);
		if (indexCount > 0)
			Indices.resize(indexCount);
		if (surfaceCount > 0)
			Surfaces.resize(surfaceCount);			
	}


	void StaticModel::Clear()
	{
		Vertices.clear();
		Indices.clear();
		Surfaces.clear();
	}
}

#endif
